Pollard ρ 算法

生日悖论

根据组合数可以得到只要一个房间里有 23 个人就有 50% 的概率有两个人的生日会相同。

k>56n=365 时,出现两个人同一天生日的概率将大于 99%

考虑一个问题,设置一个数据 n,在 [1,1000] 里随机选取 i 个数(i=1 时就是它自己),使它们之间有两个数的差值为 k。当 i=1 时成功的概率是 11000,当 i=2 时成功的概率是 1500(考虑绝对值,k2 可以取 k1kk1+k),随着 i 的增大,这个概率也会增大,最后趋向于 100%

随机数

我们设 f(x)=(x2+c)modn,其中 n 是待分解的整数;

再设数列 x 的递推式为 xi=f(xi1)。初值为 x0=1

这样可以获得足够的随机性,并且根据生日悖论,只要生成 n 个数就可以找到环。

mn 的最小非平凡因子,则一定有 1<m<n

于是我们再设数列 y,满足 i[1,],yi=ximodm。如果 i,j 使得 ij,xixj,yi=yj,我们就找到了 n 的最小非平凡因子 m

再应用一次生日悖论可以得到时间复杂度为 O(m)Θ(n14)

Floyd 判环算法

假设两个人同时同向同地开始赛跑,A 的速度快,B 的速度慢,经过一定时间后,A 一定会追上 B,且 B 第一次被追上时 A 跑过的总距离减去 B 跑过的总距离一定是圈长的 1 倍。

于是我们可以写出这样的代码:

倍增优化

上面的做法要调用很多次 gcd,导致算法乘上了一个 log

其实这个 log 是可以避免的(和常数抵消)。

修改上述结论:假设两个人同时同向同地开始赛跑,A 的速度快,B 的速度慢,经过一定时间后,A 一定会追上 B,且此时 A 跑过的总距离减去 B 跑过的总距离一定是圈长的 k(kZ+) 倍。(无论之前 B 有没有被 A 追上过)

我们每过一段时间将这些差值进行 gcd 运算。设 z=|xiyi|modn,由欧几里得算法得到 gcd(abmodn,n)=gcd(ab,n),所以 gcd(z,n)=gcd(|xiyi|,n)

如果某一时刻得到 z=0 那么表示分解失败,退出并返回 n 本身。每隔 k 个数,计算是否满足 1<gcd(z,n)<n。此处取 k=127,可以根据实际情况进行调节。

更正:由拉梅定理得到 gcd 的辗转相除次数 5log10min(n,m)=5log102×logmin(n,m),所以对于 64 位整型可取 k=100

代码

用几个小素数判一下效率会有显著提升(?)。

附带 abs(__int128)readu128()putu128()。这份代码中采用倍增优化,k=32768

这里有一个小技巧:只改变 x 在环上的位置,不改变 y 在环上的位置。可以证明这和一个前进两格、另一个前进一格是等价的。